package com.yvdelx.cyclowomen.mediation;

import com.jjoe64.graphview.GraphView.GraphViewData;
import com.yvdelx.cyclowomen.business.CycleBusiness;
import com.yvdelx.cyclowomen.business.ProfileBusiness;
import com.yvdelx.cyclowomen.connector.XmlWriter;
import com.yvdelx.cyclowomen.exception.CstException;
import com.yvdelx.cyclowomen.exception.FunctionalException;
import com.yvdelx.cyclowomen.object.Cycle;
import com.yvdelx.cyclowomen.object.Day;
import com.yvdelx.cyclowomen.object.EnumSensation;
import com.yvdelx.cyclowomen.object.Profile;
import com.yvdelx.cyclowomen.utility.DateUtility;
import com.yvdelx.cyclowomen.utility.LogUtility;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Manage the cycles
 * Created by ydelvaux on 23/02/14.
 */
public class CycleManagement {

    private static Profile profile;

    private static Cycle cycle;

    /**
     * Set the profile
     * @param pProfile Profile
     */
    public static void setProfile(Profile pProfile) {
        profile = pProfile;
    }

    /**
     * Get the current cycle
     *
     * @return Cycle currentCycle
     * @throws Exception
     */
    public static Cycle getCycleCurrent() throws Exception {
        // If profile is not initialized, then initialize it
        if (profile == null)
            profile = getProfile();
        if (cycle == null)
            cycle = ProfileBusiness.getCurrentCycle(profile);
        return cycle;
    }

    /**
     * Is it the first use on the phone ?
     *
     * @return true if it's the first use of the app
     */
    public static Boolean isFirstUse() throws IOException {
        return !XmlWriter.isFileExist();
    }


    public static Profile getProfile() throws Exception {
        // If profile doesn't exist, it's the first use, the app set the profile
        if (profile == null)
            setProfile(XmlWriter.getData());
        return profile;
    }

    /**
     * To create the profile on the first use or modify it after
     *
     * @param name      String
     * @param firstName String
     */
    public static void modifyProfile(String name, String firstName) throws Exception {
        // If profile doesn't exist, it's a creation (called by the profile activity)
        if (profile == null) {
            setProfile(new Profile(name, firstName));
        } else {
            profile.setFirstname(firstName);
            profile.setName(name);
            XmlWriter.saveData(profile);
        }

    }


    /**
     * Return the current cycle a the date in param
     *
     * @param date Date
     * @return Cycle
     */
    public static Cycle getCycle(Date date) throws Exception {
        return ProfileBusiness.getCycleByDayDate(date, profile);
    }

    /**
     * Create or modify day at the date
     *
     * @param day Day
     */
    public static void modifyDay(Day day) throws Exception {
        LogUtility.debug(CycleManagement.class, day.toString());
        Date date = day.getDate();
        EnumSensation sensation = day.getSensation();
        Cycle cycle = ProfileBusiness.getCurrentCycle(profile);
        // if no current cycle, create new cycle
        if (cycle == null) {
            // First day must be menses
            if (sensation != null && sensation.isMenses()) {
                createCycle(day);
            } else {
                throw new FunctionalException(CstException.CST_FIRST_DAY_CYCLE_NOT_MENSES);
            }
        } else {
            Day lastDay = CycleBusiness.getLastDay(cycle);
            Day actDay = CycleBusiness.getDay(date, cycle);
            if (sensation != null && sensation.isMenses()
                    && lastDay != null
                    && lastDay.getSensation() != null
                    && !lastDay.getSensation().isMenses()
                    && actDay == null) {

                // Close the actual cycle
                cycle.setDateFin(DateUtility.previousDate(date));
                cycle.setCurrentCycle(false);

                // Create the new current cycle
                createCycle(day);

            } else {
                CycleBusiness.addDay(day, cycle);
            }
        }

        XmlWriter.saveData(profile);

    }

    /**
     * Create a new cycle and initialise it with the first day
     *
     * @param day the new Day
     */
    private static void createCycle(Day day) {

        // id computed
        String newID = CycleBusiness.getNextID(profile);

        // Initialize
        cycle = new Cycle(newID);
        cycle.setCurrentCycle(true);
        CycleBusiness.addDay(day, cycle);
        ProfileBusiness.addCycle(cycle, profile);
        LogUtility.debug(CycleManagement.class, cycle.toString());
    }

    /**
     * Put newCycle as current cycle
     * @param newCycle Cycle
     */
    public static void changeCurrentCycle(Cycle newCycle) throws Exception {
        if(newCycle != null) {
            Cycle currentCycle;
            Cycle newCurrentCycle;
            if(cycle != null) {
                currentCycle = ProfileBusiness.getCycleById(cycle.getIdCycle(),profile);
                currentCycle.setCurrentCycle(false);
                XmlWriter.saveData(profile);
            }
            newCurrentCycle = ProfileBusiness.getCycleById(newCycle.getIdCycle(),profile);
            newCurrentCycle.setCurrentCycle(true);
            XmlWriter.saveData(profile);
            cycle = newCurrentCycle;
        }

    }

    /**
     * Get day at the date for cycle in param
     *
     * @param date     Date
     * @param tmpCycle Cycle
     * @return Day
     */
    public static Day getDay(Date date, Cycle tmpCycle) {
        if (tmpCycle == null)
            return null;
        if (cycle == null || !tmpCycle.equals(cycle)) {
            cycle = tmpCycle;
            cycle.setCurrentCycle(true);
        }
        return CycleBusiness.getDay(date, cycle);
    }

    /**
     * Get day at the date for current cycle
     *
     * @param date Date
     * @return Day
     */
    public static Day getDay(Date date) {
        return getDay(date, cycle);
    }


    /**
     * Delete day in the current cycle
     *
     * @param day Day (object to delete)
     */
    public static void delete(Day day) throws Exception {
        CycleBusiness.deleteDay(day, cycle);
        XmlWriter.saveData(profile);
    }

    /**
     * Return the day after or before the day in param
     *
     * @param day Day
     * @param isNextDay Boolean -- "true" : search next day, "false" : search before day
     * @return Day
     */
    public static Day getNextDay(Day day, Boolean isNextDay) {
        return CycleBusiness.getNextDay(day, cycle, isNextDay);
    }

    /**
     * Return true if the day is considered as fertile
     *
     * @param day Day
     * @return Boolean
     */
    public static Boolean isFertileDay(Day day) {

        if(day != null) {
            return !(
                    (day.getNature()!= null && day.getNature().isHighPoint())
                    || (day.getSensation() != null && day.getSensation().isDry())
            );
        } else {
            return Boolean.FALSE;
        }
    }

    /**
     * Get the date of the last day of the current cycle
     *
     * @return Date
     * @throws ParseException
     */
    public static Date getCurrentDate() throws ParseException {
        Date date =  CycleBusiness.getCurrentDate(cycle);
        if(date != null)
            return date;
        else
            return DateUtility.getDate();
    }

    /**
     * Get the series data for the graphic activity
     * @return List<GraphViewData>
     */
    public static List<GraphViewData[]> getDaysGraphViewData() {
        // Initialize
        List<GraphViewData[]> result = new ArrayList<GraphViewData[]>(3);
        Integer size = cycle.getLstDays().size();
        Integer index = 0;

        GraphViewData[] dayData = new GraphViewData[size];
        GraphViewData[] dayLowAverage = new GraphViewData[size];
        GraphViewData[] dayHighAverage = new GraphViewData[size];

        GraphViewData data;
        // For all days cycle, the three series are updated
        for (Day day : cycle.getLstDays()) {
            data = new GraphViewData(day.getDate().getTime(), day.getTemperature());
            dayData[index] = data;
            data = new GraphViewData(day.getDate().getTime(), cycle.getAverageLowTemperature());
            dayLowAverage[index] = data;
            // If no average high temperature, the variable is null because it doesn't need
            // to be draw.
            if (cycle.getAverageHighTemperature() != 0f) {
                data = new GraphViewData(day.getDate().getTime(), cycle.getAverageHighTemperature());
                dayHighAverage[index] = data;
            } else {
                dayHighAverage[index] = null;
            }
            index++;
        }

        // Put results in the result list
        result.add(0, dayData);
        result.add(1, dayLowAverage);
        result.add(2, dayHighAverage);

        return result;
    }

    /**
     * Returns the list of cycles by the date
     * @param month Integer
     * @param year Integer
     * @return List<Cycle>
     */
    public static List<Cycle> getCycles(Integer month, Integer year) {
        Date date = DateUtility.getDate(month,year);
        return ProfileBusiness.getCycles(date, profile);
    }
}
